package cn.zswltech.gruul.biz.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.zswltech.gruul.biz.comparator.MenuDOComparator;
import cn.zswltech.gruul.biz.service.FunctionService;
import cn.zswltech.gruul.biz.service.MenuService;
import cn.zswltech.gruul.biz.service.OrgService;
import cn.zswltech.gruul.biz.service.RoleService;
import cn.zswltech.gruul.biz.support.impl.IServiceImpl;
import cn.zswltech.gruul.common.constant.CommonConstants;
import cn.zswltech.gruul.common.constant.CustomTreeConstants;
import cn.zswltech.gruul.common.constant.MenuConstants;
import cn.zswltech.gruul.common.dal.dao.*;
import cn.zswltech.gruul.common.dto.*;
import cn.zswltech.gruul.common.entity.*;
import cn.zswltech.gruul.common.result.MSG;
import cn.zswltech.gruul.common.result.Response;
import cn.zswltech.gruul.web.api.util.GruulUtil;
import cn.zswltech.gruul.web.api.util.AccountUtil;
import cn.zswltech.gruul.common.dal.vo.AccountVO;
import com.alibaba.fastjson.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.Sqls;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


@Service("menuService")
@Slf4j
public class MenuServiceImpl extends IServiceImpl<MenuDO> implements MenuService {
    @Autowired
    private MenuDOMapper menuDOMapper;
    @Autowired
    private FunctionDOMapper functionDOMapper;
    @Autowired
    private CustomTreeMenuRefDOMapper customTreeMenuRefDOMapper;
    @Resource(name = "roleService")
    private RoleService roleService;
    @Resource(name = "orgService")
    private OrgService orgService;
    @Resource(name = "functionService")
    private FunctionService functionService;
    @Resource
    private OrgMenuFunctionDOMapper orgMenuFunctionDOMapper;
    @Resource
    private RoleMenuFunctionDOMapper roleMenuFunctionDOMapper;
    @Resource
    private SystemManagerMapper systemManagerMapper;


    @Override
    public Response<List<MenuDO>> getMenuList() {
        Response<List<MenuDO>> response = new Response<List<MenuDO>>();
        List<MenuDO> menuDOs = null;
        try {
            menuDOs = menuDOMapper.queryAll();
            Collections.sort(menuDOs, new MenuDOComparator());
        } catch (Exception e) {
            log.error("API get menu list error", e);
            response.setMessage(MSG.req_error_query_failed);
            return response;
        }
        // 1级目录
        List<MenuDO> menuList = new ArrayList<MenuDO>();
        // 2级目录
        Map<Long, List<MenuDO>> menul2Map = new HashMap();
        // 3级目录
        Map<Long, List<MenuDO>> menul3Map = new HashMap();
        for (MenuDO menuDO : menuDOs) {
//            if (StringUtils.isNotBlank(menuDO.getPath())) {
//                menuDO.setPath(AESUtil.decrypt(menuDO.getPath(), MD5Util.MD5(projectCodeMD5).substring(0, 16), projectCodeMD5.substring(0, 16)));
//            }
            if (menuDO.getLevel() == MenuConstants.LEVEL_1) {
                menuList.add(menuDO);
            } else if (menuDO.getLevel() == MenuConstants.LEVEL_2) {
                hendleMenuMap(menul2Map, menuDO);
            } else if (menuDO.getLevel() == MenuConstants.LEVEL_3) {
                // 3级目录的children是function列表
                menuDO.setChildren(new JSONArray(new ArrayList<>(functionDOMapper.queryByMenuId(menuDO.getId()))));
                hendleMenuMap(menul3Map, menuDO);
            }
        }
        for (MenuDO menu : menuList) {
            List<MenuDO> menul2List = menul2Map.get(menu.getId());
            if (menul2List != null && menul2List.size() > 0) {
                for (MenuDO menul2 : menul2List) {
                    List<MenuDO> ml3List = menul3Map.get(menul2.getId());
                    if (ml3List != null && ml3List.size() > 0) {
                        menul2.setChildren(new JSONArray(new ArrayList<>(ml3List)));
                    }
                }
                menu.setChildren(new JSONArray(new ArrayList<>(menul2List)));
            }
        }
        return Response.success(menuList);
    }

    @Override
    public List<MenuInfo> underGroupMenuList(MenuListREQ req) {
        if (req.getCustomTreeId() == null){
            return Collections.emptyList();
        }
        List<CustomTreeMenuRefDO> customTreeMenuRefDOS = customTreeMenuRefDOMapper.selectByExample(Example.builder(CustomTreeMenuRefDO.class).andWhere(Sqls.custom().andEqualTo("customTreeId", req.getCustomTreeId())).build());
        if (ObjectUtil.isEmpty(customTreeMenuRefDOS)){
            log.info("菜单组：{}下没有菜单", req.getCustomTreeId());
            return Collections.emptyList();
        }
        List<Long> menuIds = customTreeMenuRefDOS.stream().map(CustomTreeMenuRefDO::getMenuId).collect(Collectors.toList());
        List<MenuDO> menuDOS = menuDOMapper.selectByExample(Example.builder(MenuDO.class).andWhere(Sqls.custom().andIn("id", menuIds)).build());
        return menuDOS.stream().map(item -> buildMenuInfo(item)).collect(Collectors.toList());
    }

    /**
     * 菜单树下新增菜单
     * @param req
     */
    @Override
    @Transactional
    public void underGroupMenuAdd(UnderGroupMenuAddREQ req) {
        MenuDO menuDO = toMenuDO(req);
        menuDOMapper.save(menuDO);
        CustomTreeMenuRefDO customTreeMenuRefDO = toCustomTreeMenuRefDO(menuDO.getId(),req.getCustomTreeId());
        customTreeMenuRefDOMapper.insert(customTreeMenuRefDO);

    }

    /**
     * 菜单编辑
     * @param req
     */
    @Override
    public void underGroupMenuUpdate(UnderGroupMenuUpdateREQ req) {
        MenuDO menuDO = menuDOMapper.selectByPrimaryKey(req.getMenuId());
        menuDO.setGmtModified(new Date())
                .setUpdateBy(AccountUtil.getLoginInfo().getAccount())
                .setName(req.getName())
                .setPath(req.getPath())
                .setCode(req.getCode())
                .setSortNo(req.getSortNo());
        menuDOMapper.updateByPrimaryKeySelective(menuDO);
    }

    @Override
    @Transactional
    public void underGroupMenuDel(UnderGroupMenuDelREQ req) {
        // 菜单表
        menuDOMapper.deleteByPrimaryKey(req.getMenuId());
        // 菜单组菜单关联表
        customTreeMenuRefDOMapper.deleteByExample(Example.builder(CustomTreeMenuRefDO.class).andWhere(Sqls.custom().andEqualTo("menuId", req.getMenuId())).build());
        // 机构关联菜单表
        orgMenuFunctionDOMapper.deleteByExample(Example.builder(OrgMenuFunctionDO.class).andWhere(Sqls.custom().andEqualTo("menuId", req.getMenuId())).build());
        // 角色关联菜单表
        roleMenuFunctionDOMapper.deleteByExample(Example.builder(RoleMenuFunctionDO.class).andWhere(Sqls.custom().andEqualTo("menuId", req.getMenuId())).build());
    }

    @Override
    public List<GruulMenu> gruulMenuList(String account) {
        List<SystemManagerDO> systemManagerDOList
                = systemManagerMapper.selectByExample(Example.builder(SystemManagerDO.class).andWhere(Sqls.custom().andEqualTo("account", account)).build());
        List<MenuDO> gruulMenuList = menuDOMapper.selectByExample(Example.builder(MenuDO.class).andWhere(Sqls.custom().andEqualTo("systemCode", CommonConstants.GRUUL_MENU)).build());
        List<MenuDO> normalMenuList = gruulMenuList.stream().filter(item -> !CommonConstants.GRUUL_SYSTEM_MENU_TYPE.equals(item.getType())).collect(Collectors.toList());
        // 管理员菜单列表
        List<GruulMenu.SystemMenu> managerMenuList = buildMenu(gruulMenuList);
        // 非管理原菜单列表
        List<GruulMenu.SystemMenu> unManagerMenuList = buildMenu(normalMenuList);
        List<GruulMenu> menuList = new ArrayList<>();
        for (SystemManagerDO systemManagerDO : systemManagerDOList) {
            GruulMenu gruulMenu = new GruulMenu();
            gruulMenu.setSystemCode(systemManagerDO.getSystemCode());

        }
        return null;
    }

    /**
     * 构造菜单
     * @param gruulMenuList
     * @return
     */
    private List<GruulMenu.SystemMenu> buildMenu(List<MenuDO> gruulMenuList) {
        Map<Long, List<MenuDO>> parentId2MenuMap = gruulMenuList.stream().filter(item -> item.getParentId() != null).collect(Collectors.groupingBy(MenuDO::getParentId));
        List<MenuDO> topMenuList = gruulMenuList.stream().filter(item -> item.getParentId() == null).collect(Collectors.toList());
        for (MenuDO menuDO : topMenuList) {
            GruulMenu.SystemMenu systemMenu = new GruulMenu.SystemMenu();
            systemMenu.setCode(menuDO.getCode())
                    .setId(menuDO.getId())
                    .setName(menuDO.getName())
                    .setSortNo(menuDO.getSortNo())
                    .setPath(menuDO.getPath());
        }
        return null;
    }

    private CustomTreeMenuRefDO toCustomTreeMenuRefDO(Long menuId, Long customTreeId) {
        CustomTreeMenuRefDO customTreeMenuRefDO = new CustomTreeMenuRefDO();
        return customTreeMenuRefDO.setGmtCreate(new Date())
                .setGmtModified(new Date())
                .setCreateBy(AccountUtil.getLoginInfo().getAccount())
                .setCustomTreeId(customTreeId)
                .setMenuId(menuId);
    }

    private MenuDO toMenuDO(UnderGroupMenuAddREQ req) {
        MenuDO menuDO = new MenuDO();
        menuDO.setGmtCreate(new Date())
                .setGmtModified(new Date())
                .setCode(req.getCode())
                .setLevel(1)
                .setSortNo(req.getSortNo())
                .setPath(req.getPath())
                .setSystemCode(GruulUtil.getSystemCode())
                .setName(req.getName())
                .setCreateBy(AccountUtil.getLoginInfo().getAccount())
                .setTarget("_self");
        return menuDO;
    }

    private MenuInfo buildMenuInfo(MenuDO menuDO) {
        MenuInfo menuInfo = new MenuInfo();
        return menuInfo.setId(menuDO.getId())
                .setGmtCreate(menuDO.getGmtCreate())
                .setGmtModified(menuDO.getGmtModified())
                .setCode(menuDO.getCode())
                .setLevel(menuDO.getLevel())
                .setSortNo(menuDO.getSortNo())
                .setPath(menuDO.getPath())
                .setName(menuDO.getName());
    }

    private void hendleMenuMap(Map<Long, List<MenuDO>> menul2Map, MenuDO menuDO) {
        Long id = menuDO.getParentId();
        if (menul2Map.containsKey(id)) {
            menul2Map.get(id).add(menuDO);
        } else {
            List<MenuDO> menus = new ArrayList<MenuDO>();
            menus.add(menuDO);
            menul2Map.put(id, menus);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteMenu(Long id) {
        Response response = new Response();
        if (id == null) {
            response.setMessage(MSG.req_error_param_null, "uuid");
            return response;
        }
        try {
            MenuDO menuDO = menuDOMapper.queryById(id);
            if (menuDO == null) {
                response.setMessage(MSG.req_error_param_invalid, "uuid");
                return response;
            }
            if (menuDO.getLevel() == MenuConstants.LEVEL_3) {
                deleteMenu3(id);
            } else if (menuDO.getLevel() == MenuConstants.LEVEL_2) {
                deleteMenu2(id);
            } else if (menuDO.getLevel() == MenuConstants.LEVEL_1) {
                deleteMenuByUuid(id);
                List<MenuDO> menuDOs = menuDOMapper.queryByParentId(id);
                if (CollectionUtils.isNotEmpty(menuDOs)) {
                    for (MenuDO menu : menuDOs) {
                        deleteMenu2(menu.getId());
                    }
                }
            }
            return Response.success(menuDO.getParentId());
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            log.error("API delete menu error", e);
            response.setMessage(MSG.req_error_delete_failed);
        }
        return response;
    }

    /**
     * 删除2级目录
     *
     * @param id
     * @throws Exception
     */
    private void deleteMenu2(Long id) throws Exception {
        deleteMenuByUuid(id);
        List<MenuDO> menuDOs = menuDOMapper.queryByParentId(id);
        if (CollectionUtils.isNotEmpty(menuDOs)) {
            for (MenuDO menu : menuDOs) {
                deleteMenu3(menu.getId());
            }
        }
    }

    private void deleteMenuByUuid(Long id) {
        MenuDO menuDO = new MenuDO();
        menuDO.setId(id);
        menuDOMapper.delete(menuDO);
    }

    /**
     * 删除3级目录
     *
     * @param id
     * @throws Exception
     */
    private void deleteMenu3(Long id) throws Exception {
        deleteMenuByUuid(id);
        // 删除自定义分组和目录关联关系
        customTreeMenuRefDOMapper.deleteByMenuId(id);
        // 删除机构和目录关联关系
        orgService.deleteOrgMenuFunctions(null, id, CustomTreeConstants.OPT_TYPE_MENU, null);
        // 删除角色和目录关联关系
        roleService.deleteRoleMenuFunctions(null, id, CustomTreeConstants.OPT_TYPE_MENU, null);
        // 删除功能
        FunctionDO functionDO = new FunctionDO();
        functionDO.setMenuId(id);
        List<FunctionDO> functionDOs = functionDOMapper.select(functionDO);
        if (CollectionUtils.isNotEmpty(functionDOs)) {
            for (FunctionDO function : functionDOs) {
                functionService.deleteFunction(function.getId());
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateMenu(MenuDO menuDO) {
        Response response = new Response();
        if (menuDO.getId() == null) {
            response.setMessage(MSG.req_error_param_null, "id");
            return response;
        }
        if (menuDO.getName() != null && StringUtils.isBlank(menuDO.getName())) {
            response.setMessage(MSG.req_error_param_null, "name");
            return response;
        }
        if (menuDO.getIcon() != null && StringUtils.isBlank(menuDO.getIcon())) {
            response.setMessage(MSG.req_error_param_null, "icon");
            return response;
        }
        if (menuDO.getPath() != null && StringUtils.isBlank(menuDO.getPath())) {
            response.setMessage(MSG.req_error_param_null, "path");
            return response;
        }
        if (menuDO.getEnName() != null && StringUtils.isBlank(menuDO.getEnName())) {
            response.setMessage(MSG.req_error_param_null, "enName");
            return response;
        }
        if (menuDO.getCode() != null && StringUtils.isBlank(menuDO.getCode())) {
            response.setMessage(MSG.req_error_param_null, "code");
            return response;
        }
        try {
            menuDO.setGmtModified(new Date());
            int result = updateNotNull(menuDO);
            if (result == 0) {
                response.setMessage(MSG.req_error_param_invalid, "uuid");
                return response;
            }
            return Response.success(null);
        } catch (Exception e) {
            log.error("API update menu error", e);
            response.setMessage(MSG.req_error_modify_failed);
        }
        return response;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response<Map> createMenu(MenuDO menuDO) {
        Response response = new Response();
        if (checkParams(menuDO, response)) {
            return response;
        }
        try {
            AccountVO account = AccountUtil.getLoginInfo();
            if (account != null) {
                menuDO.setCreateBy(account.getAccount());
                menuDO.setUpdateBy(account.getAccount());
            }
            menuDOMapper.insert(menuDO);
            response.setMessage(MSG.req_error_create_success);
            return Response.success(genParamsMap("id", menuDO.getId()));
        } catch (Exception e) {
            log.error("API create menu error", e);
            if (e.getMessage().contains("uk_")) {
                return Response.error(MSG.req_error_param_code_repeat);
            }
            response.setMessage(MSG.req_error_create_failed);
        }
        return response;
    }

    private boolean checkParams(MenuDO menuDO, Response response) {
        if (StringUtils.isBlank(menuDO.getCode())) {
            response.setMessage(MSG.req_error_param_null, "code");
            return true;
        }
        if (StringUtils.isBlank(menuDO.getName())) {
            response.setMessage(MSG.req_error_param_null, "name");
            return true;
        }
        if (menuDO.getLevel() == null) {
            response.setMessage(MSG.req_error_param_null, "level");
            return true;
        }
        if (menuDO.getLevel() > MenuConstants.LEVEL_3 || menuDO.getLevel() < MenuConstants.LEVEL_1) {
            response.setMessage(MSG.req_error_param_tip, "level", "[1,2,3]");
            return true;
        }
        // 除了1级目录,其他目录parentUuid必须要传
        if (menuDO.getLevel() == MenuConstants.LEVEL_1) {
            menuDO.setParentId(null);
        } else {
            if (menuDO.getParentId() == null) {
                response.setMessage(MSG.req_error_param_null, "parentUuid");
                return true;
            }
            MenuDO menuIndb = menuDOMapper.queryById(menuDO.getParentId());
            if (menuIndb == null || menuIndb.getLevel() != menuDO.getLevel() - 1) {
                response.setMessage(MSG.req_error_param_invalid, "parentUuid");
                return true;
            }
        }
        return false;
    }
}
